package com.gitee.faster.webdav.kernel.resource.local;

import cn.hutool.core.io.FileUtil;
import cn.hutool.core.io.IoUtil;
import cn.hutool.core.io.file.FileWriter;
import cn.hutool.core.text.StrPool;
import com.gitee.faster.webdav.common.constant.Constants;
import com.gitee.faster.webdav.common.structure.RangeValue;
import com.gitee.faster.webdav.exception.ReadException;
import com.gitee.faster.webdav.kernel.Configuration;
import com.gitee.faster.webdav.kernel.resource.Resource;
import com.gitee.faster.webdav.kernel.resource.ResourceRepository;
import com.gitee.faster.webdav.kernel.resource.ResourceRepositoryFactory;
import com.gitee.faster.webdav.kernel.resource.ResourceRepositoryRemote;
import lombok.extern.slf4j.Slf4j;

import java.io.File;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.RandomAccessFile;
import java.nio.file.Files;
import java.nio.file.Path;
import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
import java.util.stream.Stream;

/**
 * @author hongda.li
 */
@Slf4j
public class LocalResourceRepository implements ResourceRepository {

    private final int defaultReadBufferSize = Configuration.getInstance().getReadBufferSize();

    private final int defaultWriteBufferSize = Configuration.getInstance().getWriteBufferSize();

    @Override
    public boolean accept(String requestPath) {
        // 默认接受所有路径请求
        return true;
    }

    @Override
    public LocalResource transform(String requestPath) {
        return new LocalResource(requestPath);
    }

    @Override
    public Resource getRoot() {
        return this.transform(StrPool.SLASH);
    }

    @Override
    public void mkdir(Resource resource) {
        if (resource instanceof LocalResource local) {
            FileUtil.mkdir(local.getFile());
        }
    }

    @Override
    public void delete(Resource resource) {
        if (resource instanceof LocalResource local) {
            FileUtil.del(local.getFile());
        }
    }

    @Override
    public List<Resource> walk(Resource resource, int depth) {
        if (!(resource instanceof LocalResource local)) {
            return Collections.emptyList();
        }

        List<Resource> resourceList = new ArrayList<>();

        if (local.isDirectory()) {
            // 如果是目录则按深度递归目录下的文件
            try (Stream<Path> stream = Files.walk(local.getFile().toPath(), depth)) {
                stream.map(path -> new File(path.toUri()))
                        .map(LocalResource::new)
                        .map(Resource.class::cast)
                        .forEach(resourceList::add);
            } catch (Exception e) {
                throw new ReadException("遍历文件出现错误", e);
            }

            // 如果是根目录，则将其它远程虚拟目录也挂载到根目录中
            if (local.isRoot()) {
                List<ResourceRepositoryRemote> remoteRepositoryList = ResourceRepositoryFactory.getInstance().getRemoteRepositoryList();
                remoteRepositoryList.forEach(repository -> resourceList.add(repository.getRoot()));
            }

        } else {
            // 如果是文件则直接返回
            resourceList.add(resource);
        }

        return resourceList;
    }

    @Override
    public void upload(Resource resource, InputStream stream) {
        if (resource instanceof LocalResource local) {
            OutputStream out = new FileWriter(local.getFile()).getOutputStream();
            IoUtil.copy(stream, out, this.defaultReadBufferSize);
        }
    }

    @Override
    public void download(Resource resource, OutputStream outputStream) {
        if (!(resource instanceof LocalResource local)) {
            return;
        }

        try (InputStream stream = FileUtil.getInputStream(local.getFile())) {
            stream.transferTo(outputStream);
            log.info("完成全量读取 : {}", resource.getName());
        } catch (Exception e) {
            log.warn("全量读取中断 : {}", e.getMessage());
        }
    }

    @Override
    public void downloadPart(Resource resource, OutputStream outputStream, RangeValue range) {
        if (!(resource instanceof LocalResource local)) {
            return;
        }

        long end = range.end();
        long start = range.start();
        long contentLength = end - start + 1;

        // 缓冲区大小
        int bufferSize = (int) Math.min(contentLength, this.defaultWriteBufferSize);

        // 读取指定位置的文件数据
        try (RandomAccessFile raf = new RandomAccessFile(local.getFile(), Constants.READ_MODE)) {
            raf.seek(start);
            byte[] data = new byte[bufferSize];
            while (true) {
                int read = raf.read(data);
                if (read == -1) {
                    break;
                }
                outputStream.write(data, 0, read);
            }
            log.info("完成断点续传 : [{} - {} / {}]", start, end, resource.length());
        } catch (Exception e) {
            log.warn("断点续传中断 : {}", e.getMessage());
        }
    }
}
